/*
 * Helstius--Basic chemistry application
 * This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 * Copyright 2012 Jaked122
 */
package org.helstius.chem;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.LinkedList;
import java.util.StringTokenizer;
import java.util.HashMap;
import java.util.TreeSet;

import org.helstius.util.Utility;

/*
 * Helstius--Basic chemistry application
 * This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 * Copyright 2012 Jaked122
 */
public class Parser {

	public HashMap<String, Integer> getcounts(String q) {
		HashMap<String, Integer> weights = new HashMap<String, Integer>();
		if (q == null)
			return weights;

		LinkedList<String> Elements = new LinkedList<String>();
		LinkedList<Integer> numbers = new LinkedList<Integer>();
		String a = "";
		String d = "";
		boolean ischaracter = false, isdigit = false;
		for (int i = 0; i < q.length(); i++) {
			if (Character.isLetter(q.charAt(i))) {
				ischaracter = true;
				isdigit = false;
			} else if (Character.isDigit(q.charAt(i))) {
				ischaracter = false;
				isdigit = true;
			}
			if (isdigit) {
				d += q.charAt(i);
				if (a.length() >= 1) {
					Elements.push(a);
					a = "";
				}
			}
			if (ischaracter) {
				if (d.length() >= 1) {
					numbers.push(Integer.parseInt(d));
					d = "";
				}
				if (Character.isUpperCase(q.charAt(i)) && a.length() >= 1) {
					Elements.push(a);
					numbers.push(1);
					a = "";
				}
				a += q.charAt(i);
				if (a.length() == 2) {
					Elements.push(a);
					a = "";
				}
			}

		}
		if (d.length() >= 1)
			numbers.push(Integer.parseInt(d));
		if (a.length() >= 1) {
			Elements.push(a);
			numbers.push(1);
		}
		boolean c = Elements.isEmpty();
		while (!Elements.isEmpty()) {
			if (weights.containsKey(Elements.peek())) {
				weights.put(Elements.peek(), weights.get(Elements.pop())
						+ (numbers.pop()));
			} else {
				weights.put(Elements.pop(), numbers.pop());
			}

		}

		return weights;

	}

	public HashMap<String, Float> getpercentages(String e) {
		HashMap<String, Float> weights = new HashMap<String, Float>();
		if (e == null)
			return weights;
		float weight = 0;
		CharBuffer q = CharBuffer.wrap(e.toCharArray());
		LinkedList<String> Elements = new LinkedList<String>();
		LinkedList<Integer> numbers = new LinkedList<Integer>();
		String a = "";
		String d = "";
		boolean ischaracter = false, isdigit = false;
		for (int i = 0; i < q.length(); i++) {
			if (Character.isLetter(q.get(i))) {
				ischaracter = true;
				isdigit = false;
			} else if (Character.isDigit(q.get(i))) {
				ischaracter = false;
				isdigit = true;
			}
			if (isdigit) {
				d += q.get(i);
				if (a.length() >= 1) {
					Elements.push(a);
					a = "";
				}
			}
			if (ischaracter) {
				if (d.length() >= 1) {
					numbers.push(Integer.parseInt(d));
					d = "";
				}
				if (Character.isUpperCase(q.get(i)) && a.length() >= 1) {
					Elements.push(a);
					numbers.push(1);
					a = "";
				}
				a += q.get(i);
				if (a.length() == 2) {
					Elements.push(a);
					a = "";
				}
			}

		}
		if (d.length() >= 1)
			numbers.push(Integer.parseInt(d));
		if (a.length() >= 1) {
			Elements.push(a);
			numbers.push(1);
		}
		boolean c = Elements.isEmpty();
		while (!Elements.isEmpty()) {
			if (weights.containsKey(Elements.peek())) {
				weights.put(Elements.peek(), weights.get(Elements.peek())
						+ (numbers.pop() * this.weights.get(Elements.pop())));
			} else {
				weights.put(Elements.peek(),
						numbers.pop() * this.weights.get(Elements.pop()));
			}

		}
		float sum = Utility.sum(weights.values().toArray(new Float[0]));
		for (String ele : weights.keySet()) {
			float sd = weights.get(ele);
			weights.put(ele, sd / sum);
		}
		return weights;

	}

	/**
	 * Get the molar weight of a compound, as parsed by this method.
	 * 
	 * @param e
	 *            The correctly formatted string.
	 * @return The float that represents the weight
	 */
	public float parsechem(String e) {
		HashMap<String, Integer> g = getcounts(e);
		float weight = 0;
		for (String a : g.keySet())
			weight += weights.get(a) * g.get(a);
		return weight;
	}

	public static boolean countsequal(HashMap<String, Boolean> a) {
		for (String e : a.keySet())
			if (!a.get(e))
				return false;
		return true;
	}

	/**
	 * Get the string representation of a comparison between tables
	 * 
	 * @param in
	 *            The table of equality
	 * @return The string of equality, or a fairly decent version of it.
	 */
	public static String getRepresentation(HashMap<String, Boolean> in) {
		StringBuilder a = new StringBuilder();
		for (String f : in.keySet()) {
			a.append(f + ":" + in.get(f).toString());
			a.append("\n");
		}
		return a.toString();
	}

	/**
	 * Provides an easy method of turning the HashMap output into a string
	 * suitable for display
	 * 
	 * @param in
	 *            The input HashMap
	 * @return An almost decent string representation of in
	 */
	public static String GetRepresentation(HashMap<String, Integer> in) {
		StringBuilder a = new StringBuilder();
		for (String f : in.keySet()) {
			a.append(f + ":" + in.get(f).toString());
			a.append("\n");
		}
		return a.toString();
	}

	public static HashMap<String, Boolean> comparecounts(
			HashMap<String, Integer> a, HashMap<String, Integer> b) {
		HashMap<String, Boolean> q = new HashMap<String, Boolean>();
		for (String f : a.keySet())
			if (a.get(f) == b.get(f))
				q.put(f, true);
			else
				q.put(f, false);
		for (String y : b.keySet())
			if (!a.containsKey(y))
				q.put(y, false);
		return q;
	}

	/**
	 * Add together the elements contained in the various panes.
	 * 
	 * @param v
	 *            The first equation counts
	 * @param c
	 *            The Second equation counts
	 * @return
	 */
	public static HashMap<String, Integer> AddMaps(HashMap<String, Integer> v,
			HashMap<String, Integer> c) {
		HashMap<String, Integer> f = new HashMap<String, Integer>();
		int q = 0;
		if (v != null)
			for (String e : v.keySet()) {
				f.put(e, v.get(e) + (c.containsKey(e) ? c.get(e) : 0));
			}
		if (c != null)
			for (String e : c.keySet())
				if (!f.containsKey(e))
					f.put(e, c.get(e));
		return f;
	}

	/**
	 * Delegate method that calls the other AddMaps function
	 * 
	 * @param v
	 * @param c
	 * @param d
	 * @return
	 * @see Parser
	 */
	public static HashMap<String, Integer> AddMaps(HashMap<String, Integer> v,
			HashMap<String, Integer> c, HashMap<String, Integer> d) {
		return AddMaps(v, AddMaps(c, d));
	}

	/**
	 * Multiply the elemental counts by an integer
	 * 
	 * @param a
	 *            The map to multiply(also the output variable)
	 * @param f
	 *            Number to multiply by
	 */
	public static void multiplyMaps(HashMap<String, Integer> a, int f) {
		for (String e : a.keySet())
			a.put(e, a.get(e) * f);

	}

	/**
	 * 
	 * Multiply the elemental counts by an integer
	 * 
	 * @param a
	 *            The map to multiply
	 * @param f
	 *            Number to multiply by
	 * @param output
	 *            The output map(cleared before assignment).
	 */
	public static void multiplyMaps(HashMap<String, Integer> a, int f,
			HashMap<String, Integer> output) {
		output.clear();
		for (String e : a.keySet())
			output.put(e, a.get(e) * f);
	}

	public Parser() {

	}

	static public HashMap<String, Float> weights;
	static {
		weights = new HashMap<String, Float>();

		String[] symbol = { "Zr", "Zn", "Y", "V", "Ti", "Te", "Tc", "Sw", "Sr",
				"Sn", "Si", "Sc", "Sb", "S", "Ru", "Rh", "Rf", "Rb", "Ra",
				"Pt", "Pd", "Pb", "P", "Os", "O", "Ni", "Ne", "Nb", "Na", "N",
				"Mo", "Mn", "Mg", "Li", "La", "K", "In", "I", "Hg", "Hf", "He",
				"H", "Ge", "Ga", "Fr", "Fe", "F", "Cu", "Cs", "Cr", "Co", "Cl",
				"Cd", "Ca", "C", "Br", "Bi", "Be", "Ba", "B", "Au", "As", "Ar",
				"Al", "Ag", "Ac" };
		double[] e = { 91.22, 65.38, 88.91, 50.94, 47.88, 128.6, 98, 78.96,
				87.62, 118.71, 28.09, 44.96, 121.75, 32.07, 101.1, 102.91, 261,
				85.47, 226.02, 195.1, 106.42, 207.2, 30.97, 190.2, 16, 58.69,
				20.18, 92.91, 22.99, 14.01, 95.94, 54.94, 23.31, 6.941, 138.91,
				39.1, 114.83, 126.91, 200.59, 178.49, 4.003, 1.008, 72.59,
				69.72, 223, 55.85, 19, 63.55, 152.91, 52, 58.93, 35.35, 112.41,
				40.08, 12.01, 79.9, 208.98, 9.012, 137.33, 10.81, 196.97,
				74.92, 39.95, 26.98, 107.87, 227.03 };
		for (int i = 0; i < symbol.length; i++) {
			weights.put(symbol[i], (float) e[i]);
		}
	}

}
